package com.boat.starter.web.config;

import java.util.Arrays;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ThreadFactory;
import java.util.concurrent.ThreadPoolExecutor;

import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.AsyncConfigurer;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import com.boat.framework.base.exception.ServiceException;
import com.boat.framework.common.constant.GolbalConsts;
import com.boat.framework.common.util.SpringUtils;
import com.boat.framework.common.util.ThreadUtils;

import cn.hutool.core.thread.ThreadUtil;
import cn.hutool.core.util.ArrayUtil;
import lombok.extern.slf4j.Slf4j;

/**
 * 异步配置
 *
 * @author Lion Li
 */
@Slf4j
@EnableAsync(proxyTargetClass = true)
@Configuration
public class AsyncAutoConfiguration implements AsyncConfigurer, DisposableBean {
	/**
	 * 核心线程数 = cpu 核心数 + 1
	 */
	private final int core = Runtime.getRuntime().availableProcessors() + 1;

	private ScheduledExecutorService scheduledExecutorService;

	/**
	 * 默认异步线程池。使用方式：@Async
	 * 
	 * @return
	 */
	@Bean(name = GolbalConsts.ThreadPool.EXECUTOR_ASYNC)
	public ThreadPoolTaskExecutor asyncExecutor() {
		ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
		executor.setCorePoolSize(core);
		executor.setMaxPoolSize(core * 2);
		executor.setQueueCapacity(2000);
		executor.setKeepAliveSeconds(60);
		executor.setThreadNamePrefix("asyncExecutor-");
		executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
		return executor;
	}

	/**
	 * 异步流程线程池。使用方式：ThreadPool.instance().runProcess(Runnable)
	 * 
	 * @return
	 */
	@Bean(name = GolbalConsts.ThreadPool.EXECUTOR_PROCESS)
	public ThreadPoolTaskExecutor processExecutor() {
		ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
		executor.setCorePoolSize(core);
		executor.setMaxPoolSize(core * 2);
		executor.setQueueCapacity(2000);
		executor.setKeepAliveSeconds(60);
		executor.setThreadNamePrefix("processExecutor-");
		executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
		return executor;
	}

	/**
	 * 周期性任务或者定时任务。
	 * 使用方式：ThreadPool.instance().getSchedule().scheduleAtFixedRate(Runnable,initialDelay,
	 * period, TimeUnit.SECONDS);
	 * 
	 * @return
	 */
	@Bean(name = GolbalConsts.ThreadPool.EXECUTOR_SCHEDULED)
	public ScheduledExecutorService scheduledExecutorService() {
		ThreadFactory namedThreadFactory = ThreadUtil.createThreadFactory("scheduledExecutor-");
		scheduledExecutorService = Executors.newScheduledThreadPool(core, namedThreadFactory);
		return scheduledExecutorService;
	}

	/**
	 * 自定义 @Async 注解使用系统线程池
	 */
	@Override
	public Executor getAsyncExecutor() {
		return SpringUtils.getBean(GolbalConsts.ThreadPool.EXECUTOR_ASYNC);
	}

	/**
	 * 异步执行异常处理
	 */
	@Override
	public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
		return (throwable, method, objects) -> {
			throwable.printStackTrace();
			StringBuilder sb = new StringBuilder();
			sb.append("Exception message - ").append(throwable.getMessage()).append(", Method name - ")
					.append(method.getName());
			if (ArrayUtil.isNotEmpty(objects)) {
				sb.append(", Parameter value - ").append(Arrays.toString(objects));
			}
			throw new ServiceException(sb.toString());
		};
	}

	@Override
	public void destroy() throws Exception {
		try {
			// log.info("====关闭后台任务任务线程池====");
			ThreadUtils.shutdownAndAwaitTermination(scheduledExecutorService);
		} catch (Exception e) {
			log.warn(e.getMessage(), e);
		}
	}
}